package com.example.gybicycle.common.wxPay;


import com.example.gybicycle.entity.MemberPay;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.cipher.PrivacyEncryptor;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * Description: 微信支付对接（基于JSAPI 支付的扩展类实现）
 *
 * @author LinHuiBa-YanAn
 * @date 2023/10/7 11:49
 */
@Slf4j
@Service
public class WechatPayExternalServiceImpl implements WechatPayExternalService {

    @Resource
    private Config config;

    @Resource
    private WxPayConfig properties;

    @Resource
    private JsapiServiceExtension jsapiServiceExtension;

    @Resource
    private NotificationParser notificationParser;

    @Override
    public PrepayWithRequestPaymentResponse prepayWithRequestPayment(MemberPay pay) {
        log.info("prepayWithRequestPayment");
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        BigDecimal payMoney = pay.getMoney();
        BigDecimal amountTotal = payMoney.multiply(new BigDecimal("100")).setScale(0, RoundingMode.DOWN);
        amount.setTotal(amountTotal.intValue());
        request.setAmount(amount);
        Payer payer = new Payer();
        payer.setOpenid(pay.getMemberOpenId());
        request.setPayer(payer);
        request.setTimeExpire(getExpiredTimeStr());
        request.setAppid(properties.getAppid());
        request.setMchid(properties.getMchId());
        request.setAttach(pay.getMemberOrderId());
        request.setDescription(pay.getRemark());
        request.setNotifyUrl(properties.getNotifyUrl());
        //这里生成流水号，后续用这个流水号与微信交互，查询订单状态
        request.setOutTradeNo(pay.getId());
        PrepayWithRequestPaymentResponse result;
        try {
            result = jsapiServiceExtension.prepayWithRequestPayment(request);
        } catch (HttpException e) {
            log.error("微信下单发送HTTP请求失败，错误信息：{}", e.getHttpRequest());
            throw new RuntimeException("微信下单发送HTTP请求失败", e);
        } catch (ServiceException e) {
            // 服务返回状态小于200或大于等于300，例如500
            log.error("微信下单服务状态错误，错误信息：{}", e.getErrorMessage());
            throw new RuntimeException("微信下单服务状态错误", e);
        } catch (MalformedMessageException e) {
            // 服务返回成功，返回体类型不合法，或者解析返回体失败
            log.error("服务返回成功，返回体类型不合法，或者解析返回体失败，错误信息：{}", e.getMessage());
            throw new RuntimeException("服务返回成功，返回体类型不合法，或者解析返回体失败", e);
        }
        log.info("prepayWithRequestPayment end");
        return result;
    }

    @Override
    public Transaction queryStatus(String outTradeNo) {
        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        request.setMchid(properties.getMchId());
        request.setOutTradeNo(outTradeNo);
        try {
            return jsapiServiceExtension.queryOrderByOutTradeNo(request);
        } catch (ServiceException e) {
            log.error("订单查询失败，返回码：{},返回信息：{}", e.getErrorCode(), e.getErrorMessage());
            throw new RuntimeException("订单查询失败", e);
        }
    }

    @Override
    public void closeOrder(String outTradeNo) {
        log.info("closeOrder");
        CloseOrderRequest closeRequest = new CloseOrderRequest();
        closeRequest.setMchid(properties.getMchId());
        closeRequest.setOutTradeNo(outTradeNo);
        try {
            //方法没有返回值，意味着成功时API返回204 No Content
            jsapiServiceExtension.closeOrder(closeRequest);
        } catch (ServiceException e) {
            log.error("订单关闭失败，返回码：{},返回信息：{}", e.getErrorCode(), e.getErrorMessage());
            throw new RuntimeException("订单关闭失败", e);
        }
    }



    @Override
    public Refund refund(MemberPay pay,String refundId) {
        try {
            // 构建退款service
            RefundService service = new RefundService.Builder().config(config).build();
            // request.setXxx(val)设置所需参数，具体参数可见Request定义
            //构建退款请求
            CreateRequest request = new CreateRequest();
            //构建订单金额信息
            AmountReq amountReq = new AmountReq();
            BigDecimal payMoney = pay.getMoney();
            BigDecimal amountTotal = payMoney.multiply(new BigDecimal("100")).setScale(0, RoundingMode.DOWN);
            Long money=Long.valueOf(amountTotal.intValue());

            //退款金额
            amountReq.setRefund(money);
            //原订单金额
            amountReq.setTotal(money);
            //货币类型(默认人民币)
            amountReq.setCurrency("CNY");
            request.setAmount(amountReq);
            request.setOutTradeNo(pay.getId());
            //商户退款单号
            request.setOutRefundNo(refundId);
            //退款通知回调地址
            request.setNotifyUrl(properties.getRefundnotifyUrl());

            // 调用退款方法，得到应答
            // 调用微信sdk接口
            Refund refund = service.create(request);
            return refund;

        }catch (ServiceException e){
           e.printStackTrace();
        }catch (Exception e){
             e.printStackTrace();
        }
        return null;
    }
    @Override
    public <T> T payCallBack(CallBackBody wechatPayCallBackRequest, Class<T> clazz) {
        log.info("payCallBack");
        PrivacyEncryptor privacyEncryptor = config.createEncryptor();
        String weChatPayCertificateSerialNumber = privacyEncryptor.getWechatpaySerial();
        if (!wechatPayCallBackRequest.getSerial().equals(weChatPayCertificateSerialNumber)) {
            log.error("证书不一致");
            throw new RuntimeException("证书不一致");
        }
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(wechatPayCallBackRequest.getSerial())
                .nonce(wechatPayCallBackRequest.getNonce()) //resource.nonce
                .signType(wechatPayCallBackRequest.getSignatureType())
                .signature(wechatPayCallBackRequest.getSignature())
                .timestamp(wechatPayCallBackRequest.getTimestamp())
                .body(wechatPayCallBackRequest.getBody())
                .build();
        var re=notificationParser.parse(requestParam,clazz);
        return re;
    }
    public RefundNotification refundNotify(HttpServletRequest request)  {
        try {
            String line;
            StringBuilder sb = new StringBuilder();
            BufferedReader reader = request.getReader();
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
//            String s1=sb.toString();
//            String timestamp = request.getHeader("WECHAT_PAY_TIMESTAMP");
//            String nonce = request.getHeader("WECHAT_PAY_NONCE");
//            String signType = request.getHeader("Wechatpay-Signature-Type");
//            String serialNo = request.getHeader("WECHAT_PAY_SERIAL");
//            String signature = request.getHeader("WECHAT_PAY_SIGNATURE");
//            System.out.println("timestamp="+timestamp);
//            System.out.println("nonce="+nonce);
//            System.out.println("signType="+signType);
//            System.out.println("serialNo="+serialNo);
//            System.out.println("signature="+signature);
//            System.out.println("data="+s1);
            CallBackBody backBody = new CallBackBody();
            backBody.setSerial(request.getHeader("Wechatpay-Serial"));
            backBody.setNonce(request.getHeader("Wechatpay-Nonce"));
            backBody.setTimestamp(request.getHeader("Wechatpay-Timestamp"));
            backBody.setSignature(request.getHeader("Wechatpay-Signature"));
            backBody.setSignatureType(request.getHeader("Wechatpay-Signature-Type"));
            backBody.setBody(sb.toString());
            System.out.println("backBody="+backBody);
            RequestParam requestParam = new RequestParam.Builder()
                    .serialNumber(backBody.getSerial())
                    .nonce(backBody.getNonce()) //resource.nonce
                    .signType(backBody.getSignatureType())
                    .signature(backBody.getSignature())
                    .timestamp(backBody.getTimestamp())
                    .body(backBody.getBody())
                    .build();
            // 如果已经初始化了 RSAAutoCertificateConfig，可直接使用
         /*   RequestParam requestParam=new RequestParam.Builder()
                    .serialNumber(serialNo)
                    .nonce(nonce)
                    .signature(signature)
                    .timestamp(timestamp)
                    // 若未设置signType，默认值为 WECHATPAY2-SHA256-RSA2048
                    .signType(signType)
                    .body(s1)
                    .build();*/
            RefundNotification parse = notificationParser.parse(requestParam, RefundNotification.class);
            System.out.println("parse = " + parse);
            if (parse.getRefundStatus().equals(Transaction.TradeStateEnum.SUCCESS)){
                //你的业务代码
                System.out.println("退款");
            }
            return  parse;
        } catch (Exception e) {

        }
        return null;
    }

    /**
     * 获取失效时间
     */
    private String getExpiredTimeStr() {
        //失效时间，10分钟
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime expiredTime = now.plusMinutes(10);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        return formatter.format(expiredTime);
    }


}
